package trade

import (
	"context"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/smartwalle/alipay/v3"
	"go.uber.org/zap"

	"mxshop-api/order-web/api"
	"mxshop-api/order-web/api/order"
	"mxshop-api/order-web/forms"
	"mxshop-api/order-web/global"
	orderpb "mxshop-api/order-web/proto/order/gen/v1"
	"mxshop-api/order-web/trade"
	ali "mxshop-api/order-web/trade/alipay"
	"mxshop-api/shared/auth"
)

func Payment(c *gin.Context) {
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	var paymentParams forms.PaymentParams
	err = c.ShouldBind(&paymentParams)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	// 1.校验订单是否属于当前用户
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	detailResp, err := orderCli.OrderDetail(context.Background(), &orderpb.OrderDetailRequest{
		OrderId: paymentParams.OrderID,
		UserId:  int32(userID),
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	// 判断支付状态是否为等待买家付款。只有当订单状态为WAIT_BUYER_PAY才允许创建支付。
	switch detailResp.Order.Order.Status {
	case orderpb.OrderStatus_ORDER_STATUS_TRADE_CREATION:
	case orderpb.OrderStatus_ORDER_STATUS_WAIT_BUYER_PAY:
	default:
		c.JSON(http.StatusBadRequest, gin.H{"msg": "the order has been paid"})
		return
	}

	orderInfo := order.EntityGrpcToAPIOrder(detailResp.Order)

	// 2.根据payment type去拿到对应的第三方支付
	var trader trade.Trader
	switch paymentParams.Type {
	case 1: // 支付宝支付
		trader = ali.NewAlipay(global.AlipayCli)
	case 2: // 微信支付
		c.JSON(http.StatusNotFound, gin.H{"error": "wechat payment is unimplemented"})
		return
	default:
		c.JSON(http.StatusBadRequest, gin.H{"error": "payment_type must be in the range of 1 to 2"})
		return
	}

	// 3.修改订单状态以及支付方式
	orderSn, err := strconv.Atoi(orderInfo.OrderSn)
	if err != nil {
		zap.L().Error("failed to parse order sn", zap.Error(err))
		c.Status(http.StatusInternalServerError)
		return
	}
	_, err = orderCli.UpdateOrderStatus(context.Background(), &orderpb.UpdateOrderStatusRequest{
		OrderId:     orderInfo.ID,
		OrderSn:     int64(orderSn),
		Status:      orderpb.OrderStatus_ORDER_STATUS_PAYING,
		PaymentType: orderpb.PayType(paymentParams.Type),
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	// 4.调用sdk生成预支付交易
	paymentURL, err := trader.Payment(
		orderInfo.OrderSn,
		orderInfo.Total,
		"",
	)
	if err != nil {
		zap.L().Error("payment service unavailable",
			zap.String("name", orderpb.PayType(paymentParams.Type).String()),
		)
		c.JSON(http.StatusBadGateway, "")
		return
	}

	// 5.返回预支付交易链接
	c.JSON(http.StatusOK, gin.H{"payment_url": paymentURL})
}

func Notification(c *gin.Context) {
	cli := ali.NewAlipay(global.AlipayCli)
	notify, err := cli.Notification(c.Request)
	if err != nil {
		c.Status(http.StatusBadRequest)
		return
	}

	// 需要给支付宝一个ACK响应。
	// SDK给出的方式:alipay.ACKNotification(writer),这里改为gin的响应方式
	c.String(http.StatusOK, "success")

	// 支付成功修改订单状态
	orderSn, err := strconv.Atoi(notify.OutTradeNo)
	if err != nil {
		zap.L().Error("failed to convert string to int", zap.Error(err))
	}
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	_, err = orderCli.UpdateOrder(context.Background(), &orderpb.UpdateOrderRequest{
		OrderSn: int64(orderSn),
		Status:  orderpb.OrderStatus_ORDER_STATUS_TRADE_SUCCESS,
		TradeNo: notify.TradeNo,
	})
	if err != nil {
		zap.L().Error("failed to changes order status", zap.Int("order_sn", orderSn), zap.Error(err))
	}
}

func GetTrade(c *gin.Context) {
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	var id forms.ID
	err = c.ShouldBindUri(&id)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	// 1.校验订单是否属于当前用户
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	detailResp, err := orderCli.OrderDetail(context.Background(), &orderpb.OrderDetailRequest{
		OrderId: id.ID,
		UserId:  int32(userID),
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	// 2.判断支付状态是否为支付中。只有当订单状态为PAYING才允许更改订单信息。
	switch detailResp.Order.Order.Status {
	case orderpb.OrderStatus_ORDER_STATUS_PAYING:
	case orderpb.OrderStatus_ORDER_STATUS_TRADE_SUCCESS:
		c.Status(http.StatusOK)
		return
	default:
		c.JSON(http.StatusBadRequest, gin.H{"msg": "the order status is not paying"})
		return
	}

	// 3.从支付宝中查询订单信息
	orderSn := strconv.Itoa(int(detailResp.Order.Order.OrderSn))
	cli := ali.NewAlipay(global.AlipayCli)
	resp, err := cli.TradeQuery(orderSn)
	if err != nil {
		c.Status(http.StatusBadGateway)
		return
	}

	// 4.1 支付未成功直接返回
	if resp.Content.TradeStatus != alipay.TradeStatusSuccess {
		c.Status(http.StatusBadRequest)
		return
	}

	// 4.2 支付成功更新订单状态
	_, err = orderCli.UpdateOrder(context.Background(), &orderpb.UpdateOrderRequest{
		OrderSn: detailResp.Order.Order.OrderSn,
		Status:  orderpb.OrderStatus_ORDER_STATUS_TRADE_SUCCESS,
		TradeNo: resp.Content.TradeNo,
	})

	c.Status(http.StatusOK)
}
